/*
* Copyright (C) 2024-2025 Uniontech Technology Co., Ltd.
*
* Author:      zhangbingyi <zhangbingyia@uniontech.com>
*
* Maintainer:  zhangbingyi <zhangbingyia@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "tptablemodel.h"
#include "trustedprotectiondefinition.h"
#include "invokers/invokerfactory.h"
#include "common.h"
#include "hconcurrent.h"
//#include <DApplication>
//#include <DApplicationHelper>
//#include <DPalette>
//#include <DStyleHelper>
//#include <DStyle>
//#include <DGuiApplicationHelper>
//#include <DPalette>
//#include <DLabel>
//#include <DFontSizeManager>

#include <QApplication>
#include <QPalette>
#include <QStyle>
#include <QLabel>
#include <QFileInfo>
#include <QPaintDevice>
#include <QThread>

QString getNameFromPath(const QString path)
{
    QFileInfo file(path);
    // 迭代修改，不再需要判断文件是否存在
    //    if (file.exists()) {
    //        return file.baseName();
    //    }
    //    return "";

    return file.fileName();
}

TPTableModel::TPTableModel(QObject *parent)
    : QStandardItemModel(parent)
    , m_IMAInvoker(nullptr)
    , m_monitorInvoker(nullptr)
    , m_initThread(nullptr)
    , m_isRequestStop(false)
{
    registerTPPolicyInfoMetaType();
    registerTPPolicyInfoListMetaType();

    QStandardItem *standaritem0 = new QStandardItem(tr("Object"));
    standaritem0->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    QStandardItem *standaritem1 = new QStandardItem(tr("Path"));
    standaritem1->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    QStandardItem *standaritem2 = new QStandardItem(tr("Status"));
    standaritem2->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    QStandardItem *standaritem3 = new QStandardItem(tr("Protection Policy"));
    this->setHorizontalHeaderItem(0, standaritem0);
    this->setHorizontalHeaderItem(1, standaritem1);
    this->setHorizontalHeaderItem(2, standaritem2);
    this->setHorizontalHeaderItem(3, standaritem3);

    m_comboIconMap.insert(CustomPolicyWarn, ICON_WARNING);
    //m_comboIconMap.insert(CustomPolicyAllow, ICON_WARNING); // todo allow icon
    m_comboIconMap.insert(CustomPolicyDeny, ICON_FORBID);

    m_IMAInvoker = InvokerFactory::GetInstance().CreateInvoker("com.deepin.daemon.ImaDaemon",
                                                               "/com/deepin/daemon/ImaDaemon",
                                                               "com.deepin.daemon.ImaDaemon",
                                                               ConnectType::SYSTEM, this);

    m_monitorInvoker = InvokerFactory::GetInstance().CreateInvoker("com.deepin.defender.MonitorNetFlow",
                                                                   "/com/deepin/defender/MonitorNetFlow",
                                                                   "com.deepin.defender.MonitorNetFlow",
                                                                   ConnectType::SYSTEM, this);

    initConnection();
}

TPTableModel::~TPTableModel()
{
    if (m_initThread) {
        if (m_initThread->isRunning()) {
            m_isRequestStop = true;
            m_initThread->quit();
            m_initThread->wait();
        }

        m_initThread->deleteLater();
        m_initThread = nullptr;
    }
}

void TPTableModel::initData()
{
    // 为了不发生异常，当接口不存在时，不再查询数据和添加，前端无数据时不会产生操作
    if (!m_IMAInvoker) {
        return;
    }

    // DBUS 接口获取当前系统已加入的策略
    Coroutine co;
    std::thread th = std::thread(&TPTableModel::getPolicyList, this, &co);
    while (th.joinable()) {
        QApplication::processEvents();
        th.join();
    }
    co.GetValue();
    Q_EMIT NoticeModelInitFinished();
}

void TPTableModel::initConnection()
{
    if (m_IMAInvoker) {
        m_IMAInvoker->Connect("Notification", this, SLOT(onSyncObject(const QString &, const QString &)));
        m_IMAInvoker->Connect("FsNotification", this, SLOT(onIMAComponentRemoved(const QString &)));
    }
}

void TPTableModel::getPolicyList(Coroutine* co)
{
    QDBusMessage msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "GetPolicyList");
    GET_MESSAGE_VALUE(TPPolicyInfoList, policyList, msg);
    foreach (auto policy, policyList) {
        if (m_isRequestStop) {
            break;
        }
        insertPolicy(policy);
    }
    co->SetValue(0);
}

QVariant TPTableModel::data(const QModelIndex &index, int role) const
{
    // T8字体
    if (role == Qt::FontRole) {
//        QFont font = DFontSizeManager::instance()->t8();
//        return font;
        return qApp->font();
    }

    // 字体颜色控制
    if (STATUS_COLUMN != index.column() && role == Qt::ForegroundRole) {
        QModelIndex statusIndex = this->index(index.row(), STATUS_COLUMN);
        // 如果文件不存在则置灰
        if (!statusIndex.data(FILE_EXIST_STATUS_ROLE).toBool()) {
            // 我们使用的表格在没有设置时即是灰色
            // 因此不需要特意设置
            // 保留此处注释，避免意外修改
        } else {
            QLabel lable;
            QPalette pal;
            return pal.brush(lable.foregroundRole());
        }
    }

    // 状态列显示内容与字体控制
    if (STATUS_COLUMN == index.column()) {
        // 显示内容
        if (role == Qt::DisplayRole) {
            if (!index.data(FILE_EXIST_STATUS_ROLE).toBool()) {
                // 根据迭代要求，如果发生文件未删除而策略删除，则状态修改为“文件丢失”
                return QVariant(tr("Deleted"));
            }

            if (NORMAL == index.data(ABNORMAL_STATUS_ROLE).toInt()) {
                return QVariant(tr("Normal"));
            } else {
                return QVariant(tr("Tampered"));
            }
        }

        if (role == Qt::ToolTipRole) {
            if (!index.data(FILE_EXIST_STATUS_ROLE).toBool()) {
                return QVariant(tr("File deleted"));
            }
        }
        // 字体颜色
        QBrush fontBrash;
        if (role == Qt::ForegroundRole) {
            // 文件不存在时置灰
            if (index.data(FILE_EXIST_STATUS_ROLE).toBool()) {
                if (ABNORMAL == index.data(ABNORMAL_STATUS_ROLE).toInt()) {
                    return QBrush(QColor(ABNORMAL_FONT_BG_COLOR));
                }

                if (NORMAL == index.data(ABNORMAL_STATUS_ROLE).toInt()) {
                    QLabel lable;
                    QPalette pal;
                    return pal.brush(lable.foregroundRole());
                }
            }
        }
    }

    // 根据策略值 设置combox, 设置图标
    if (POLICY_COLUMN == index.column()) {
        if (role == Qt::DecorationRole) {
            int comboState = index.data(CUSTOM_POLICY_TYPE_ROLE).toInt();
            QString path = m_comboIconMap.value(comboState);
            if (path.isEmpty()) {
                path = m_comboIconMap.value(CustomPolicyDeny);
            }
            return QIcon(path);
        }
    }

    return QStandardItemModel::data(index, role);
}

bool TPTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // 如果存在数值问题，在这里纠正

    if (index.column() == POLICY_COLUMN) {
        if (role == CUSTOM_POLICY_TYPE_ROLE) {
            // 获取策略内容并对比，一致则不处理
            int row = index.row();
            int typeValue = value.toInt();
            //QString policyTargePath = data(index(index.row(), PATH_COLUMN), Qt::DisplayRole).toString();
            QString policyTargePath = this->index(row, PATH_COLUMN).data().toString();
            QDBusMessage msg;
            QString errorInfo;
            msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "GetPolicy", policyTargePath);
            errorInfo = msg.errorMessage();
            if (errorInfo.isEmpty()) {
                GET_MESSAGE_VALUE(TPPolicyInfo, policy, msg);
                if (policy.customPolicyType != typeValue) {
                    TPPolicyInfoList policyList;

                    // 根据接口调用，在发生修改时变更参数类型
                    policy.policyModifyType = IMA_POLICY_MODIFY_UPDATE;
                    policy.customPolicyType = typeValue;
                    policyList.append(policy);
                    // 修改策略并更新到IMA
                    DBUS_BLOCK_INVOKE(m_IMAInvoker, "AddPolicy", QVariant::fromValue(policyList));

                    // 记录此处的策略修改
                    QString logInfo;
                    switch (policy.customPolicyType) {
                    case CustomPolicyDeny:
                        logInfo = tr("Changed the protection policy of %1 to: Prohibit");
                        break;
                    case CustomPolicyWarn:
                        logInfo = tr("Changed the protection policy of %1 to: Warning");
                        break;
                    }
                    if (!logInfo.isEmpty()) {
                        DBUS_NOBLOCK_INVOKE(m_monitorInvoker, "AddSecurityLog", SECURITY_LOG_TYPE_TOOL, logInfo.arg(policy.targetPath));
                    }
                }
            }
        }
    }
    return QStandardItemModel::setData(index, value, role);
}

// 重写remove方法,我们一次只移除一行
// 参数count应当始终为1
bool TPTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    // 如果功能不存在，不执行
    if (!m_IMAInvoker) {
        return false;
    }

    // 每次调用只允许移除一行
    if (count != 1) {
        return false;
    }

    // 先调用dbus删除底层记录
    QDBusMessage msg;
    QString errorInfo;
    TPPolicyInfoList policyList;

    // 删除该策略
    // 通过路径直接查询当前策略内容，不要管界面上是什么
    QString policyTargePath = data(index(row, PATH_COLUMN), Qt::DisplayRole).toString();
    msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "GetPolicy", policyTargePath);
    errorInfo = msg.errorMessage();
    if (errorInfo.isEmpty()) {
        // 策略还存在时,说明是从界面移除,通知IMA删除策略
        // 否则是因文件删除造成的
        GET_MESSAGE_VALUE(TPPolicyInfo, policy, msg);
        policyList.append(policy);

        msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "RemovePolicy", QVariant::fromValue(policyList));
        if (!msg.errorMessage().isEmpty()) {
            return false;
        }
    }

    // 移除时增加日志
    QString logInfo = tr("Removed the protected component: %1");
    QString compnentPath = data(QModelIndex(this->index(row, PATH_COLUMN))).toString();
    DBUS_NOBLOCK_INVOKE(m_monitorInvoker, "AddSecurityLog", SECURITY_LOG_TYPE_TOOL, logInfo.arg(compnentPath));

    return QStandardItemModel::removeRows(row, count, parent);
}

// 从dbus调用返回向model插入行
void TPTableModel::insertPolicy(const TPPolicyInfo &policy)
{
    if (policy.targetPath.isEmpty()) {
        return;
    }

    if (policy.policyType == POLICY_DEFAULT) {
        return;
    }

    // 检查是否已经存在此对象的策略
    QList<QStandardItem *> updateItems = findItems(policy.targetPath, Qt::MatchExactly, PATH_COLUMN);
    if (!updateItems.count()) {
        // 不存在时,在model中插入
        // 勾选项
        QStandardItem *c0 = new QStandardItem;
        c0->setData(false, CHECKED_STATUS_ROLE);
        c0->setData(policy.policyType, POLICY_TYPE_ROLE); // 用于判断是否可勾选
        // 对象名称
        QString objFileName = getNameFromPath(policy.targetPath);
        c0->setData(objFileName, Qt::DisplayRole);

        // 路径
        QStandardItem *c1 = new QStandardItem;
        c1->setData(policy.targetPath, Qt::DisplayRole);

        // 状态，是否正常
        QStandardItem *c2 = new QStandardItem;
        // 调用接口检查状态再填充
        // 先设置为正常，如果接口检查正常，会将其修改为准确状态
        c2->setData(NORMAL, ABNORMAL_STATUS_ROLE);
        QDBusMessage msg;
        if (m_IMAInvoker) {
            msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "Appraise", policy.targetPath);
            // error msg : maybe "has no xattr"
            if (msg.errorMessage().isEmpty()) {
                GET_MESSAGE_VALUE(bool, isNormal, msg);
                int tmp = isNormal ? NORMAL : ABNORMAL;
                c2->setData(tmp, ABNORMAL_STATUS_ROLE);

                if (!isNormal) {
                    QString logInfo = tr("Tampered component: %1");
                    DBUS_NOBLOCK_INVOKE(m_monitorInvoker, "AddSecurityLog", SECURITY_LOG_TYPE_TOOL, logInfo.arg(policy.targetPath));
                }
            }
        }
        // 文件如果已删除，则需要修改displayRole内容
        c2->setData(QFileInfo(policy.targetPath).exists(), FILE_EXIST_STATUS_ROLE);

        // 策略类型
        QStandardItem *c3 = new QStandardItem;
        c3->setData(policy.customPolicyType, CUSTOM_POLICY_TYPE_ROLE);
        c3->setData(policy.policyType, POLICY_TYPE_ROLE); // 记录策略类型，以判断是否禁用下拉框

        // 状态列与策略列增加tooltip
        c2->setData(tr("If modified, show \"tampered\"; otherwise, show \"normal\""), Qt::ToolTipRole);

        QString policyHoverNotice = tr("Prohibit: if the integrity is damaged, prohibit the execution") + "\n" + tr("Warning: if the integrity is damaged, show warnings when executed");
        c3->setData(policyHoverNotice, Qt::ToolTipRole);

        this->appendRow({c0, c1, c2, c3});

        Q_EMIT NoticeRowInserted();
    } else {
        // 优化 删除 ： 如果是重复插入，什么都不做
        // 不主动更新策略，让手户自己修改
        //QList<QStandardItem *> updateItems = findItems(policy.targetPath, Qt::MatchExactly, POLICY_COLUMN);
        //        foreach (auto item, updateItems) {
        //            item->setData(policy.customPolicyType, CUSTOM_POLICY_TYPE_ROLE);
        //        }
    }
}

// 从UI界面向dbus接口插入策略
void TPTableModel::insertPolicy(const QString &path)
{
    // 如果功能不存在，不执行
    if (!m_IMAInvoker) {
        return;
    }
    QDBusMessage msg;
    TPPolicyInfoList policyList;
    // 除了路径，其它值都使用默认值，不需要修改
    TPPolicyInfo newPolicy;
    newPolicy.targetPath = path;
    policyList.append(newPolicy);

    msg = DBUS_BLOCK_INVOKE(m_IMAInvoker, "AddPolicy", QVariant::fromValue(policyList));
    if (!msg.errorMessage().isEmpty()) {
        // 插入错误返回
        return;
    }

    insertPolicy(newPolicy);
}

void TPTableModel::onSyncObject(const QString &path, const QString &eventID)
{
    Q_UNUSED(eventID);

    if (path.isEmpty()) {
        return;
    }

    // 只有一个对象
    QList<QStandardItem *> updateItems = findItems(path, Qt::MatchExactly, PATH_COLUMN);
    foreach (QStandardItem *item, updateItems) {
        QModelIndex index = this->index(item->row(), STATUS_COLUMN);
        this->setData(index, ABNORMAL, ABNORMAL_STATUS_ROLE);
    }
}

void TPTableModel::onIMAComponentRemoved(const QString &path)
{
    if (path.isEmpty()) {
        return;
    }

    QList<QStandardItem *> updateItems = findItems(path, Qt::MatchExactly, PATH_COLUMN);
    foreach (QStandardItem *item, updateItems) {
        // 需求变动 ： 文件刪除不再删除策略对象
        // 转而设置文件的移除标记
        // removeRow(item->row());
        QStandardItem *statusItem = this->item(item->row(), STATUS_COLUMN);
        // 文件已经删除了，置为false以修改界面状态
        statusItem->setData(false, FILE_EXIST_STATUS_ROLE);
        statusItem->setData(ABNORMAL, ABNORMAL_STATUS_ROLE);
    }
}
